package com.pfsbuilder.algorithm;

import java.awt.Point;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;

import com.pfsbuilder.CommonLib;
import com.pfsbuilder.Global;
import com.pfsbuilder.Library;
import com.pfsbuilder.PExceptionDialog;
import com.pfsbuilder.PFSImageConstants;
import com.pfsbuilder.PFSSettingConstants;
import com.pfsbuilder.PIni;
import com.pfsbuilder.Dialogs.ReadDialog_TableModel;

public class PFSReadImageAlgorithm {
	JDialog parent;
	JTable JTable;
	ReadDialog_TableModel pReadDialog_TableModel;
	JScrollPane JScrollPane;
	JProgressBar JProgressBar;
	JButton pPauseButton;
	public boolean shouldStop;
	int numOfFileRead;
	JLabel pInfoLabel;
	long readFileIndirectBlock_TimeStamp;
	RandomAccessFile br;
	PIni ini;

	public PFSReadImageAlgorithm() {
	}

	public void readPFS() {
		readPFS(null, null, null, null, null, null, null);
	}

	public void readPFS(JDialog parent, JTable JTable, ReadDialog_TableModel pReadDialog_TableModel, JScrollPane JScrollPane, JProgressBar JProgressBar, JButton pPauseButton,
			JLabel pInfoLabel) {
		this.parent = parent;
		this.JTable = JTable;
		this.pReadDialog_TableModel = pReadDialog_TableModel;
		this.JScrollPane = JScrollPane;
		this.JProgressBar = JProgressBar;
		this.pPauseButton = pPauseButton;
		this.pInfoLabel = pInfoLabel;
		ini = new PIni(PFSSettingConstants.projectFile);
		start();
		if (Global.GUI) {
			pPauseButton.setVisible(false);
		} else {
			System.out.println("\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\rFinished                                      ");
		}

	}

	private void start() {
		try {
			if (com.pfsbuilder.Global.GUI) {
				if (shouldStop) {
					return;
				}
				while (pPauseButton.getText().equals("Resume")) {
					// Thread.currentThread().sleep(1000);
					Thread.sleep(1000);
				}
			}
			numOfFileRead = 0;
			CommonLib.deleteDirectory(PFSSettingConstants.tempDirectory);
			new File(PFSSettingConstants.tempDirectory).mkdir();
			showResult("SB", "");
			br = new RandomAccessFile(PFSSettingConstants.filename, "r");
			byte superBlock[] = new byte[PFSImageConstants.superBlockSize];
			br.seek(PFSImageConstants.partitionOffset);
			br.read(superBlock);

			if (!Library.checkMagicNumber(superBlock, 0)) {
				if (Global.GUI) {
					JOptionPane.showMessageDialog(parent, "Wrong fs format", "Error", JOptionPane.ERROR_MESSAGE);
				} else {
					System.out.println("Erorr : Wrong fs format");
				}
				br.close();
				return;
			} else {
				PFSSettingConstants.partitionName = new String(superBlock).substring(19, 519).trim();

				PFSSettingConstants.version = "2006";

				long rootDirectoryLink = (long) (((long) (superBlock[520]) & 0xff) + (((long) (superBlock[521]) & 0xff) << 8) + (((long) (superBlock[522]) & 0xff) << 16)
						+ (((long) (superBlock[523]) & 0xff) << 24) + (((long) (superBlock[524]) & 0xff) << 32) + (((long) (superBlock[525]) & 0xff) << 40)
						+ (((long) (superBlock[526]) & 0xff) << 48) + (((long) (superBlock[527]) & 0xff) << 56));

				PFSSettingConstants.blockSize = (superBlock[544] + (superBlock[545] << 8)) * 1024;
				PFSImageConstants.blockSize = PFSSettingConstants.blockSize;

				long numberOfFreeAddressBlock = (long) (((long) (superBlock[546]) & 0xff) + (((long) (superBlock[547]) & 0xff) << 8) + (((long) (superBlock[548]) & 0xff) << 16)
						+ (((long) (superBlock[549]) & 0xff) << 24) + (((long) (superBlock[550]) & 0xff) << 32) + (((long) (superBlock[551]) & 0xff) << 40)
						+ (((long) (superBlock[552]) & 0xff) << 48) + (((long) (superBlock[553]) & 0xff) << 56));

				byte fab[] = new byte[(int) (PFSImageConstants.superBlockSize * numberOfFreeAddressBlock)];
				br.seek(PFSImageConstants.partitionOffset + PFSImageConstants.superBlockSize);
				br.read(fab);

				if (com.pfsbuilder.Global.GUI) {
					pReadDialog_TableModel.setValueAt("done", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
				}

				showResult("Block size", String.valueOf(PFSImageConstants.blockSize));
				showResult("Number of fab", String.valueOf(numberOfFreeAddressBlock));
				showResult("root Directory Link", String.valueOf(rootDirectoryLink));

				if (com.pfsbuilder.Global.GUI) {
					pReadDialog_TableModel.setValueAt("done", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
				}
				readDirectory(rootDirectoryLink, null);
			}
			br.close();
		} catch (Exception ex) {
			ex.printStackTrace();
			if (com.pfsbuilder.Global.GUI) {
				new PExceptionDialog(ex, com.pfsbuilder.Global.sessionNumber).setVisible(true);
			}
			if (com.pfsbuilder.Global.stopWhenException) {
				System.exit(0);
			}
		}
	}

	public String[] readDir(long blockNumber, String currentPath) {
		Vector<String> r = new Vector<String>();
		try {
			String dirName_entry = "";

			byte data[] = new byte[PFSImageConstants.blockSize];
			long offset = PFSImageConstants.partitionOffset + PFSImageConstants.superBlockSize + blockNumber * PFSImageConstants.blockSize;
			if (offset < 0) {
				return null;
			}
			br.seek(offset);
			br.read(data);
			// /////////////// directory name //////////////////////////
			dirName_entry = CommonLib.trimByteZero(new String(data).substring(3, 503));
			showResult("Directory : " + currentPath + "/" + dirName_entry, "");

			// /////////////// numberOfFile //////////////////////////
			long numberOfFile = (long) (((long) (data[503]) & 0xff) + (((long) (data[504]) & 0xff) << 8) + (((long) (data[505]) & 0xff) << 16)
					+ (((long) (data[506]) & 0xff) << 24));
			// /////////////// numberOfDirectory //////////////////////////
			long numberOfDirectory = (long) (((long) (data[507]) & 0xff) + (((long) (data[508]) & 0xff) << 8) + (((long) (data[509]) & 0xff) << 16)
					+ (((long) (data[510]) & 0xff) << 24));
			// /////////////// Permission //////////////////////////
			String permission = String.valueOf((char) data[511] & 0xff) + String.valueOf((char) data[512] & 0xff) + String.valueOf((char) data[513] & 0xff)
					+ String.valueOf((char) data[514] & 0xff) + String.valueOf((char) data[515] & 0xff) + String.valueOf((char) data[516] & 0xff)
					+ String.valueOf((char) data[517] & 0xff) + String.valueOf((char) data[518] & 0xff) + String.valueOf((char) data[519] & 0xff);

			File selectedFile = new File(PFSSettingConstants.tempDirectory + File.separator + currentPath + File.separator + dirName_entry);
			if (data[511] == 'r') {
				ini.setStringProperty(selectedFile.toString(), "owner read", "true", null);
			} else {
				ini.setStringProperty(selectedFile.toString(), "owner read", "false", null);
			}
			if (data[512] == 'w') {
				ini.setStringProperty(selectedFile.toString(), "owner write", "true", null);
			} else {
				ini.setStringProperty(selectedFile.toString(), "owner write", "false", null);
			}
			if (data[513] == 'x') {
				ini.setStringProperty(selectedFile.toString(), "owner execute", "true", null);
			} else {
				ini.setStringProperty(selectedFile.toString(), "owner execute", "false", null);
			}
			ini.save();

			// /////////////// createTime //////////////////////////
			long createTime = (long) ((long) ((data[520]) & 0xff) + ((long) ((data[521]) & 0xff) << 8) + ((long) ((data[522]) & 0xff) << 16) + ((long) ((data[523]) & 0xff) << 24)
					+ ((long) ((data[524]) & 0xff) << 32) + ((long) ((data[525]) & 0xff) << 40) + ((long) ((data[526]) & 0xff) << 48) + (((long) (data[527]) & 0xff) << 56));
			// /////////////// lastModifiedTime //////////////////////////
			long lastModifiedTime = (long) (((long) (data[528]) & 0xff) + (((long) (data[529]) & 0xff) << 8) + (((long) (data[530]) & 0xff) << 16)
					+ (((long) (data[531]) & 0xff) << 24) + (((long) (data[532]) & 0xff) << 32) + (((long) (data[533]) & 0xff) << 40) + (((long) (data[534]) & 0xff) << 48)
					+ (((long) (data[535]) & 0xff) << 56));
			// /////////////// directoryIndirectBlock //////////////////////////
			long directoryIndirectBlock = (long) (((long) (data[536]) & 0xff) + (((long) (data[537]) & 0xff) << 8) + (((long) (data[538]) & 0xff) << 16)
					+ (((long) (data[539]) & 0xff) << 24) + (((long) (data[540]) & 0xff) << 32) + (((long) (data[541]) & 0xff) << 40) + (((long) (data[542]) & 0xff) << 48)
					+ (((long) (data[543]) & 0xff) << 56));

			// create all the files
			if (com.pfsbuilder.Global.GUI) {
				pReadDialog_TableModel.setValueAt("done", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
			} else {
				System.out.print("\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
			}
			for (int x = 544, z = 0; x < data.length - 9; x += 9, z++) {

				long link = (long) ((long) ((data[x + 1]) & 0xff) + ((long) ((data[x + 2]) & 0xff) << 8) + ((long) ((data[x + 3]) & 0xff) << 16)
						+ ((long) ((data[x + 4]) & 0xff) << 24) + ((long) ((data[x + 5]) & 0xff) << 32) + ((long) ((data[x + 6]) & 0xff) << 40)
						+ ((long) ((data[x + 7]) & 0xff) << 48) + ((long) ((data[x + 8]) & 0xff) << 56));
				if (link != 0) {
					if (data[x] == 0) {
						if (currentPath == null) {
							if (!readFile(link, "")) {
								return null;
							}
						} else {
							if (!readFile(link, currentPath + File.separator + dirName_entry)) {
								return null;
							}
						}
					} /*
						* else { if (currentPath == null) { if
						* (!readDirectory(link, "")) { return null; } } else { if
						* (!readDirectory(link, currentPath + File.separator +
						* dirName_entry)) { return null; } } }
						*/
				}
			}
			if (directoryIndirectBlock != 0) {
				if (currentPath == null) {
					readDIB(directoryIndirectBlock, "");
				} else {
					readDIB(directoryIndirectBlock, currentPath + File.separator + dirName_entry);
				}
			}
			return (String[]) r.toArray();
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
	}

	private boolean readDirectory(long blockNumber, String currentPath) {
		try {
			if (com.pfsbuilder.Global.GUI) {
				if (shouldStop) {
					return false;
				}
				while (pPauseButton.getText().equals("Resume")) {
					// Thread.currentThread().sleep(1000);
					Thread.sleep(1000);
				}
			}
			String dirName_entry = "";

			byte data[] = new byte[PFSImageConstants.blockSize];
			long offset = PFSImageConstants.partitionOffset + PFSImageConstants.superBlockSize + blockNumber * PFSImageConstants.blockSize;
			if (offset < 0) {
				return false;
			}
			br.seek(offset);
			br.read(data);
			// /////////////// directory name //////////////////////////
			dirName_entry = CommonLib.trimByteZero(new String(data).substring(3, 503));
			showResult("Directory : " + currentPath + "/" + dirName_entry, "");

			// /////////////// numberOfFile //////////////////////////
			long numberOfFile = (long) (((long) (data[503]) & 0xff) + (((long) (data[504]) & 0xff) << 8) + (((long) (data[505]) & 0xff) << 16)
					+ (((long) (data[506]) & 0xff) << 24));
			// /////////////// numberOfDirectory //////////////////////////
			long numberOfDirectory = (long) (((long) (data[507]) & 0xff) + (((long) (data[508]) & 0xff) << 8) + (((long) (data[509]) & 0xff) << 16)
					+ (((long) (data[510]) & 0xff) << 24));
			// /////////////// Permission //////////////////////////
			String permission = String.valueOf((char) data[511] & 0xff) + String.valueOf((char) data[512] & 0xff) + String.valueOf((char) data[513] & 0xff)
					+ String.valueOf((char) data[514] & 0xff) + String.valueOf((char) data[515] & 0xff) + String.valueOf((char) data[516] & 0xff)
					+ String.valueOf((char) data[517] & 0xff) + String.valueOf((char) data[518] & 0xff) + String.valueOf((char) data[519] & 0xff);

			File selectedFile = new File(PFSSettingConstants.tempDirectory + File.separator + currentPath + File.separator + dirName_entry);
			if (data[511] == 'r') {
				ini.setStringProperty(selectedFile.toString(), "owner read", "true", null);
			} else {
				ini.setStringProperty(selectedFile.toString(), "owner read", "false", null);
			}
			if (data[512] == 'w') {
				ini.setStringProperty(selectedFile.toString(), "owner write", "true", null);
			} else {
				ini.setStringProperty(selectedFile.toString(), "owner write", "false", null);
			}
			if (data[513] == 'x') {
				ini.setStringProperty(selectedFile.toString(), "owner execute", "true", null);
			} else {
				ini.setStringProperty(selectedFile.toString(), "owner execute", "false", null);
			}
			ini.save();

			// /////////////// createTime //////////////////////////
			long createTime = (long) ((long) ((data[520]) & 0xff) + ((long) ((data[521]) & 0xff) << 8) + ((long) ((data[522]) & 0xff) << 16) + ((long) ((data[523]) & 0xff) << 24)
					+ ((long) ((data[524]) & 0xff) << 32) + ((long) ((data[525]) & 0xff) << 40) + ((long) ((data[526]) & 0xff) << 48) + (((long) (data[527]) & 0xff) << 56));
			// /////////////// lastModifiedTime //////////////////////////
			long lastModifiedTime = (long) (((long) (data[528]) & 0xff) + (((long) (data[529]) & 0xff) << 8) + (((long) (data[530]) & 0xff) << 16)
					+ (((long) (data[531]) & 0xff) << 24) + (((long) (data[532]) & 0xff) << 32) + (((long) (data[533]) & 0xff) << 40) + (((long) (data[534]) & 0xff) << 48)
					+ (((long) (data[535]) & 0xff) << 56));
			// /////////////// directoryIndirectBlock //////////////////////////
			long directoryIndirectBlock = (long) (((long) (data[536]) & 0xff) + (((long) (data[537]) & 0xff) << 8) + (((long) (data[538]) & 0xff) << 16)
					+ (((long) (data[539]) & 0xff) << 24) + (((long) (data[540]) & 0xff) << 32) + (((long) (data[541]) & 0xff) << 40) + (((long) (data[542]) & 0xff) << 48)
					+ (((long) (data[543]) & 0xff) << 56));
			// create directory
			if (currentPath != null) {
				if (!new File(PFSSettingConstants.tempDirectory + File.separator + currentPath + File.separator + dirName_entry).mkdir()) {
					System.out.println("cannot create directory >" + PFSSettingConstants.tempDirectory + File.separator + currentPath + File.separator + dirName_entry + "<");
					return false;
				}
			}
			// create all the files
			if (com.pfsbuilder.Global.GUI) {
				pReadDialog_TableModel.setValueAt("done", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
			} else {
				System.out.print("\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
			}
			for (int x = 544, z = 0; x < data.length - 9; x += 9, z++) {
				if (com.pfsbuilder.Global.GUI) {
					if (shouldStop) {
						return false;
					}
					while (pPauseButton.getText().equals("Resume")) {
						// Thread.currentThread().sleep(1000);
						Thread.sleep(1000);
					}
				}

				long link = (long) ((long) ((data[x + 1]) & 0xff) + ((long) ((data[x + 2]) & 0xff) << 8) + ((long) ((data[x + 3]) & 0xff) << 16)
						+ ((long) ((data[x + 4]) & 0xff) << 24) + ((long) ((data[x + 5]) & 0xff) << 32) + ((long) ((data[x + 6]) & 0xff) << 40)
						+ ((long) ((data[x + 7]) & 0xff) << 48) + ((long) ((data[x + 8]) & 0xff) << 56));
				if (link != 0) {
					if (data[x] == 0) {
						if (currentPath == null) {
							if (!readFile(link, "")) {
								return false;
							}
						} else {
							if (!readFile(link, currentPath + File.separator + dirName_entry)) {
								return false;
							}
						}
					} else {
						if (currentPath == null) {
							if (!readDirectory(link, "")) {
								return false;
							}
						} else {
							if (!readDirectory(link, currentPath + File.separator + dirName_entry)) {
								return false;
							}
						}
					}
				}
			}
			if (directoryIndirectBlock != 0) {
				if (currentPath == null) {
					readDIB(directoryIndirectBlock, "");
				} else {
					readDIB(directoryIndirectBlock, currentPath + File.separator + dirName_entry);
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			if (com.pfsbuilder.Global.GUI) {
				new PExceptionDialog(ex, com.pfsbuilder.Global.sessionNumber).setVisible(true);
				pReadDialog_TableModel.setValueAt("fail", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
			}
			if (com.pfsbuilder.Global.stopWhenException) {
				System.exit(0);
			}
		}
		return true;
	}

	private void readDIB(long blockNumber, String currentPath) {
		try {
			if (com.pfsbuilder.Global.GUI) {
				if (shouldStop) {
					return;
				}
				while (pPauseButton.getText().equals("Resume")) {
					// Thread.currentThread().sleep(1000);
					Thread.sleep(1000);
				}
			}

			showResult("DIB", "block number = " + blockNumber + ", currentPath=" + currentPath);

			byte data[] = new byte[PFSImageConstants.blockSize];
			br.seek(PFSImageConstants.partitionOffset + PFSImageConstants.superBlockSize + blockNumber * PFSImageConstants.blockSize);
			br.read(data);

			// for (int x = 3; x < data.length - 9 - 8; x += 9) { may be wrong,
			// no -8
			for (int x = 3; x < data.length - 9; x += 9) {
				if (com.pfsbuilder.Global.GUI) {
					if (shouldStop) {
						return;
					}
					while (pPauseButton.getText().equals("Resume")) {
						// Thread.currentThread().sleep(1000);
						Thread.sleep(1000);
					}
				}

				long link = (long) (((long) (data[x + 1]) & 0xff) + (((long) (data[x + 2]) & 0xff) << 8) + (((long) (data[x + 3]) & 0xff) << 16)
						+ (((long) (data[x + 4]) & 0xff) << 24) + (((long) (data[x + 5]) & 0xff) << 32) + (((long) (data[x + 6]) & 0xff) << 40)
						+ (((long) (data[x + 7]) & 0xff) << 48) + (((long) (data[x + 8]) & 0xff) << 56));
				if (link != 0) {
					if (data[x] == 0) {
						readFile(link, currentPath);
					} else {
						readDirectory(link, currentPath);
					}
				}
			}
			if (com.pfsbuilder.Global.GUI) {
				pReadDialog_TableModel.setValueAt("done", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
			}
			long directoryIndirectBlock = (long) (((long) (data[PFSSettingConstants.blockSize - 8]) & 0xff) + (((long) (data[PFSSettingConstants.blockSize - 7]) & 0xff) << 8)
					+ (((long) (data[PFSSettingConstants.blockSize - 6]) & 0xff) << 16) + (((long) (data[PFSSettingConstants.blockSize - 5]) & 0xff) << 24)
					+ (((long) (data[PFSSettingConstants.blockSize - 4]) & 0xff) << 32) + (((long) (data[PFSSettingConstants.blockSize - 3]) & 0xff) << 40)
					+ (((long) (data[PFSSettingConstants.blockSize - 2]) & 0xff) << 48) + (((long) (data[PFSSettingConstants.blockSize - 1]) & 0xff) << 56));

			if (directoryIndirectBlock != 0) {
				readDIB(directoryIndirectBlock, currentPath);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			if (Global.GUI) {
				pReadDialog_TableModel.setValueAt("fail", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
			}
			if (com.pfsbuilder.Global.stopWhenException) {
				System.exit(0);
			}
		}
	}

	private boolean readFile(long blockNumber, String currentPath) {
		try {
			readFileIndirectBlock_TimeStamp = new Date().getTime();

			if (com.pfsbuilder.Global.GUI) {
				if (shouldStop) {
					return false;
				}
				while (pPauseButton.getText().equals("Resume")) {
					// Thread.currentThread().sleep(1000);
					Thread.sleep(1000);
				}
			}

			byte data[] = new byte[PFSImageConstants.blockSize];
			br.seek(PFSImageConstants.partitionOffset + PFSImageConstants.superBlockSize + blockNumber * PFSImageConstants.blockSize);
			br.read(data);
			// /////////////// filename //////////////////////////
			String filename_entry = CommonLib.trimByteZero(new String(data).substring(4, 504));

			// /////////////// Permission //////////////////////////
			String permission = String.valueOf((char) data[504] & 0xff) + String.valueOf((char) data[505] & 0xff) + String.valueOf((char) data[506] & 0xff)
					+ String.valueOf((char) data[507] & 0xff) + String.valueOf((char) data[508] & 0xff) + String.valueOf((char) data[509] & 0xff)
					+ String.valueOf((char) data[510] & 0xff) + String.valueOf((char) data[511] & 0xff) + String.valueOf((char) data[512] & 0xff);
			// /////////////// createTime //////////////////////////
			long createTime = (long) (((long) (data[513]) & 0xff) + (((long) (data[514]) & 0xff) << 8) + (((long) (data[515]) & 0xff) << 16) + ((long) ((data[516]) & 0xff) << 24)
					+ (((long) (data[517]) & 0xff) << 32) + (((long) (data[518]) & 0xff) << 40) + (((long) (data[519]) & 0xff) << 48) + (((long) (data[520]) & 0xff) << 56));
			// /////////////// lastModifiedTime //////////////////////////
			long lastModifiedTime = (long) (((long) (data[521]) & 0xff) + (((long) (data[522]) & 0xff) << 8) + (((long) (data[523]) & 0xff) << 16)
					+ (((long) (data[524]) & 0xff) << 24) + (((long) (data[525]) & 0xff) << 32) + ((long) ((data[526]) & 0xff) << 40) + (((long) (data[527]) & 0xff) << 48)
					+ (((long) (data[528]) & 0xff) << 56));
			// /////////////// fileSize //////////////////////////
			long fileSize = (long) (((long) (data[529]) & 0xff) + (((long) (data[530]) & 0xff) << 8) + (((long) (data[531]) & 0xff) << 16) + (((long) (data[532]) & 0xff) << 24)
					+ (((long) (data[533]) & 0xff) << 32) + (((long) (data[534]) & 0xff) << 40) + (((long) (data[535]) & 0xff) << 48) + (((long) (data[536]) & 0xff) << 56));

			// showResult((numOfFileRead + 1) + ") Block number=" + blockNumber
			// + ", File (" + CommonLib.convertFilesize(fileSize) + "): "
			// + PFSSettingConstants.tempDirectory + currentPath
			// + File.separator + filename_entry, "");

			// showResult("File" + (numOfFileRead + 1) + ") "
			// + PFSSettingConstants.tempDirectory + currentPath
			// + File.separator + filename_entry + " ("
			// + CommonLib.convertFilesize(fileSize) + "," + blockNumber
			// + ")", "");

			showResult("File" + (numOfFileRead + 1) + ") " + currentPath + "/" + filename_entry + " (" + CommonLib.convertFilesize(fileSize) + "," + blockNumber + ")", "");

			if (fileSize < 0) {
				if (com.pfsbuilder.Global.GUI) {
					pReadDialog_TableModel.setValueAt("File size error", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
				} else {
					System.out.println("File size error");
				}
				return false;
			}
			// /////////////// fibLink //////////////////////////
			long fibLink = (long) (((long) (data[537]) & 0xff) + (((long) (data[538]) & 0xff) << 8) + (((long) (data[539]) & 0xff) << 16) + (((long) (data[540]) & 0xff) << 24)
					+ (((long) (data[541]) & 0xff) << 32) + (((long) (data[542]) & 0xff) << 40) + (((long) (data[543]) & 0xff) << 48) + (((long) (data[544]) & 0xff) << 56));
			// /////////////// links //////////////////////////
			if (!new File(PFSSettingConstants.tempDirectory + File.separator + currentPath + File.separator + filename_entry).createNewFile()) {
				System.out.println("Cannot create new file : >" + PFSSettingConstants.tempDirectory + File.separator + currentPath + File.separator + filename_entry + "<");
				return false;
			}
			int offset = 0;
			for (int x = 545, z = 0; x <= data.length - 8; x += 8, z++) {
				if (com.pfsbuilder.Global.GUI) {
					if (shouldStop) {
						return false;
					}
					while (pPauseButton.getText().equals("Resume")) {
						// Thread.currentThread().sleep(1000);
						Thread.sleep(1000);
					}
				}

				long link = (long) (((long) (data[x]) & 0xff) + (((long) (data[x + 1]) & 0xff) << 8) + (((long) (data[x + 2]) & 0xff) << 16) + (((long) (data[x + 3]) & 0xff) << 24)
						+ (((long) (data[x + 4]) & 0xff) << 32) + (((long) (data[x + 5]) & 0xff) << 40) + (((long) (data[x + 6]) & 0xff) << 48)
						+ (((long) (data[x + 7]) & 0xff) << 56));

				if (link != 0) {
					offset += readFileContent(link, PFSSettingConstants.tempDirectory + File.separator + currentPath + File.separator + filename_entry, offset, fileSize);
					if (com.pfsbuilder.Global.GUI) {
						pReadDialog_TableModel.setValueAt((offset * 100 / fileSize) + "%", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
					} else {
						CommonLib.printFilesize(offset, fileSize);
					}
				}
			}

			if (fibLink != 0) {
				readFileIndirectBlock(fibLink, currentPath + File.separator + filename_entry, offset, fileSize);
			}
			numOfFileRead++;
			if (com.pfsbuilder.Global.GUI) {
				pReadDialog_TableModel.setValueAt("done", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
				pInfoLabel.setText(numOfFileRead + " files read");
			} else {
				System.out.print("\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r");
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			if (com.pfsbuilder.Global.GUI) {
				new PExceptionDialog(ex, com.pfsbuilder.Global.sessionNumber).setVisible(true);
				pReadDialog_TableModel.setValueAt("fail", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
			}
			if (com.pfsbuilder.Global.stopWhenException) {
				System.exit(0);
			}
		}
		return true;
	}

	private void readFileIndirectBlock(long blockNumber, String filepath, long offset, long fileSize) {
		try {
			if (com.pfsbuilder.Global.GUI) {
				if (shouldStop) {
					return;
				}
				while (pPauseButton.getText().equals("Resume")) {
					// Thread.currentThread().sleep(1000);
					Thread.sleep(1000);
				}
			}

			byte data[] = new byte[PFSImageConstants.blockSize];
			br.seek(PFSImageConstants.partitionOffset + PFSImageConstants.superBlockSize + blockNumber * PFSImageConstants.blockSize);
			br.read(data);

			if (data[0] == 'F' && data[1] == 'I' && data[2] == 'B') {
				for (int x = 3; x <= PFSSettingConstants.blockSize - 8 - 8; x += 8) {
					if (com.pfsbuilder.Global.GUI) {
						if (shouldStop) {
							return;
						}
						while (pPauseButton.getText().equals("Resume")) {
							// Thread.currentThread().sleep(1000);
							Thread.sleep(1000);
						}
					}

					long link = (long) (((byte) (data[x]) & 0xff) + (((byte) (data[x + 1]) & 0xff) << 8) + (((byte) (data[x + 2]) & 0xff) << 16)
							+ (((byte) (data[x + 3]) & 0xff) << 24) + (((byte) (data[x + 4]) & 0xff) << 32) + (((byte) (data[x + 5]) & 0xff) << 40)
							+ (((byte) (data[x + 6]) & 0xff) << 48) + (((byte) (data[x + 7]) & 0xff) << 56));
					if (link == 0) {
						break;
					}
					offset += readFileContent(link, PFSSettingConstants.tempDirectory + File.separator + filepath, offset, fileSize);
					if (com.pfsbuilder.Global.GUI) {
						pReadDialog_TableModel.setValueAt((offset * 100 / fileSize) + "%", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
					} else {
						CommonLib.printFilesize(offset, fileSize);
						System.out.print(String.format("%.2f", ((float) offset / (new Date().getTime() - readFileIndirectBlock_TimeStamp)) / 1024) + "MB/s    ");

					}
				}
				if (offset < fileSize) {
					long fibLink = (long) (((long) (data[PFSSettingConstants.blockSize - 8]) & 0xff) + (((long) (data[PFSSettingConstants.blockSize - 7]) & 0xff) << 8)
							+ (((long) (data[PFSSettingConstants.blockSize - 6]) & 0xff) << 16) + (((long) (data[PFSSettingConstants.blockSize - 5]) & 0xff) << 24)
							+ (((long) (data[PFSSettingConstants.blockSize - 4]) & 0xff) << 32) + (((long) (data[PFSSettingConstants.blockSize - 3]) & 0xff) << 40)
							+ (((long) (data[PFSSettingConstants.blockSize - 2]) & 0xff) << 48) + (((long) (data[PFSSettingConstants.blockSize - 1]) & 0xff) << 56));
					if (fibLink != 0) {
						readFileIndirectBlock(fibLink, filepath, offset, fileSize);
					}
				}
			} else {
				showResult("Error", "FIB header incorrect");
				return;
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			if (com.pfsbuilder.Global.GUI) {
				new PExceptionDialog(ex, com.pfsbuilder.Global.sessionNumber).setVisible(true);
				pReadDialog_TableModel.setValueAt("fail", pReadDialog_TableModel.getNormalRowCount() - 1, 1);
			}
			if (com.pfsbuilder.Global.stopWhenException) {
				System.exit(0);
			}
		}
	}

	private long readFileContent(long blockNumber, String filePath, long offset, long fileSize) {
		try {
			if (com.pfsbuilder.Global.GUI) {
				if (shouldStop) {
					return 0;
				}
				while (pPauseButton.getText().equals("Resume")) {
					// Thread.currentThread().sleep(1000);
					Thread.sleep(1000);
				}
			}

			byte data[] = new byte[PFSImageConstants.blockSize];
			br.seek(PFSImageConstants.partitionOffset + PFSImageConstants.superBlockSize + blockNumber * PFSImageConstants.blockSize);
			br.read(data);

			RandomAccessFile bw = new RandomAccessFile(filePath, "rw");
			bw.seek(offset);
			long len;
			if (fileSize - offset > PFSImageConstants.blockSize) {
				len = data.length;
				bw.write(data);
			} else {
				len = fileSize - offset;
				bw.write(data, 0, (int) (fileSize - offset));
			}
			bw.close();
			return len;
		} catch (Exception ex) {
			System.out.println(offset + "/" + fileSize);
			ex.printStackTrace();
			if (com.pfsbuilder.Global.GUI) {
				new PExceptionDialog(ex, com.pfsbuilder.Global.sessionNumber).setVisible(true);
			}
			if (com.pfsbuilder.Global.stopWhenException) {
				System.exit(0);
			}
			return 0;
		}
	}

	private void showResult(String columneOneString, String columneTwoString) {
		if (com.pfsbuilder.Global.GUI) {
			pReadDialog_TableModel.add(columneOneString, columneTwoString);
			JScrollPane.getViewport().setViewPosition(new Point(0, JTable.getMaximumSize().height));
		} else {
			System.out.println(columneOneString + " " + columneTwoString);
		}
	}
}
